package com.ezlcp.form.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ezlcp.commons.base.db.BaseDao;
import com.ezlcp.commons.base.db.BaseService;
import com.ezlcp.commons.base.entity.JsonResult;
import com.ezlcp.commons.constant.Constants;
import com.ezlcp.commons.constant.StatusEnum;
import com.ezlcp.commons.exception.GetLockTimeoutException;
import com.ezlcp.commons.service.impl.SuperServiceImpl;
import com.ezlcp.commons.tool.StringUtils;
import com.ezlcp.commons.utils.ContextUtil;
import com.ezlcp.commons.utils.TokenUtil;
import com.ezlcp.form.entity.SerialNo;
import com.ezlcp.form.mapper.SerialNoMapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

import static com.ezlcp.commons.constant.Constants.COL_TENANT_ID;


/**
 * [單據編號，系统初始化公司ID为空的记录，每个公司创建后从初始记录复制]业务服务类
 */
@Service
public class SerialNoServiceImpl extends SuperServiceImpl<SerialNoMapper, SerialNo> implements BaseService<SerialNo> {
    /**
     * redis锁KEY前缀
     */
    private static final String LOCK_KEY = "LOCK_SN:";
    @Resource
    RedisLockRegistry redisLockRegistry;
    @Autowired
    private SerialNoMapper serialNoMapper;

    /***
     * @description 根据单号规则中的占位符获取重置类型
     * @param rules 单号规则
     * @return int 重置类型
     * @author Elwin ZHANG
     * @date 2023/4/24 17:02
     */
    public int getResetType(String rules) {
        if (StringUtils.isEmpty(rules)) {
            return 0;
        }
        if (rules.indexOf("dd}") > 0 || rules.indexOf("DD}") > 0) {
            return 1;
        }
        if (rules.indexOf("MM}") > 0 || rules.indexOf("mm}") > 0) {
            return 2;
        }
        if (rules.indexOf("yy}") > 0) {
            return 4;
        }
        return 0;
    }

    /**
     * @param identifier 单据编号标识符
     * @return java.lang.String
     * 根据标识符获取单据编号
     */
    private String getSerialNo(String identifier) {
        String tenantId = ContextUtil.getCurrentTenantId();
        tenantId = TokenUtil.dealTenantId(tenantId);
        String no = genSerialNo(identifier, tenantId);
        return no;
    }

    /**
     * @param pkId 单据编号ID
     * @return java.lang.String
     * 根据ID获取单据编号
     */
    public String getSerialNoById(String pkId) {
        String tenantId = ContextUtil.getCurrentTenantId();
        tenantId = TokenUtil.dealTenantId(tenantId);
        String no = genSerialNoById(pkId, tenantId);
        return no;
    }

    /***
     * @description 生成单据编号
     * @param serialNo 单号设置对象
     * @return java.lang.String
     * @author Elwin ZHANG
     * @date 2023/4/23 17:23
     */
    public String genOrderNo(SerialNo serialNo) {
        if (serialNo == null) {
            return "";
        }
        String tenantId = ContextUtil.getCurrentTenantId();
        tenantId = TokenUtil.dealTenantId(tenantId);
        String identifier = serialNo.getIdentifier();
        String key = LOCK_KEY + tenantId + ":" + identifier;
        Lock lock = redisLockRegistry.obtain(key);
        boolean rtn = false;
        try {
            rtn = lock.tryLock(5, TimeUnit.SECONDS);
            if (rtn) {
                try {
                    return generateNo(serialNo);
                } finally {
                    lock.unlock();
                }
            } else {
                throw new GetLockTimeoutException(identifier);
            }
        } catch (InterruptedException e) {
            throw new GetLockTimeoutException(identifier);
        }
    }

    /**
     * @param pkId     单据编号ID
     * @param tenantId 公司ID
     * @return java.lang.String 生成的编号
     * 根据单据编号ID和公司ID生成编号
     */
    private String genSerialNoById(String pkId, String tenantId) {
        String key = LOCK_KEY + tenantId + ":" + pkId;
        Lock lock = redisLockRegistry.obtain(key);
        boolean rtn = false;
        try {
            rtn = lock.tryLock(5, TimeUnit.SECONDS);
            if (rtn) {
                try {
                    return generateNobyId(pkId);
                } finally {
                    lock.unlock();
                }
            } else {
                throw new GetLockTimeoutException(pkId);
            }
        } catch (InterruptedException e) {
            throw new GetLockTimeoutException(pkId);
        }
    }

    /**
     * @param identifier 单据类型
     * @param tenantId   公司ID
     * @return java.lang.String 生成的编号
     * @description: 根据单据类型和公司ID生成编号
     * @author Elwin ZHANG
     * @date 2022/5/20 17:45
     */
    private String genSerialNo(String identifier, String tenantId) {
        String key = LOCK_KEY + tenantId + ":" + identifier;
        Lock lock = redisLockRegistry.obtain(key);
        boolean rtn = false;
        try {
            rtn = lock.tryLock(5, TimeUnit.SECONDS);
            if (rtn) {
                try {
                    return generateNo(identifier);
                } finally {
                    lock.unlock();
                }
            } else {
                throw new GetLockTimeoutException(identifier);
            }
        } catch (InterruptedException e) {
            throw new GetLockTimeoutException(identifier);
        }
    }

    /**
     * @param serialNo 校验规则
     * @return com.hkyctech.commons.base.entity.JsonResult
     * @author Elwin ZHANG
     * @date 2022/5/24 15:31
     */
    public JsonResult validRule(SerialNo serialNo) {
        String rules = serialNo.getRules();
        short length = serialNo.getNoLen();
        //规则不能这空
        if (StringUtils.isEmpty(rules)) {
            return JsonResult.Fail("orderNo.rulesNotEmpty");
        }
        //数值长度不小于3
        if (length < 3) {
            return JsonResult.Fail("orderNo.noLengthLess3");
        }
        //有且只能有一个{no}
        int indexF = rules.toLowerCase().indexOf("{no}");
        int indexL = rules.toLowerCase().lastIndexOf("{no}");
        if (indexF < 0 || indexF != indexL) {
            return JsonResult.Fail("orderNo.OnlyOneNo");
        }
        String no = getByRule(rules, length, 999);
        return JsonResult.getSuccessResult(no, "common.handleSuccess");
    }

    /**
     * 根据规则返回需要显示的单据编号
     *
     * @param rule     单据编号 规则。
     * @param length   流水号的长度。
     * @param curValue 流水号的当前值。
     * @return 单据编号
     */
    private String getByRule(String rule, short length, long curValue) {
        Calendar now = Calendar.getInstance();
        NumberFormat nf = new DecimalFormat("00");
        int year = now.get(Calendar.YEAR);
        int month = now.get(Calendar.MONTH) + 1;
        int day = now.get(Calendar.DATE);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append("0");
        }
        SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat shortDateFormat = new SimpleDateFormat("yyyMM");
        NumberFormat seqFt = new DecimalFormat(sb.toString());
        String seqNo = seqFt.format(curValue);
        return rule.replace("{yyyy}", year + "")
                .replace("{yy}", String.valueOf(year).substring(2, 4))
                .replace("{MM}", nf.format(month))
                .replace("{mm}", month + "")
                .replace("{DD}", nf.format(day))
                .replace("{dd}", day + "")
                .replace("{NO}", seqNo)
                .replace("{no}", curValue + "")
                .replace("{yyyyMM}", shortDateFormat.format(now.getTime()))
                .replace("{yyyyMMDD}", fullDateFormat.format(now.getTime()));
    }

    /***
     * @description: 获取某公司的某个单擗编号配置
     * @param identifier 标识符
     * @return com.ezlcp.form.entity.SerialNo
     * @author Elwin ZHANG
     * @date 2022/5/20 18:15
     */
    public SerialNo getByIdentifier(String identifier) {
        String tenantId = ContextUtil.getCurrentTenantId();
        QueryWrapper<SerialNo> wrapper = new QueryWrapper<>();
        if (StringUtils.isEmpty(tenantId)) {
            wrapper.and(query -> query.eq(COL_TENANT_ID, "").or().isNull(COL_TENANT_ID));
        } else {
            wrapper.eq(COL_TENANT_ID, tenantId);
        }
        wrapper.eq("identifier", identifier);
        return serialNoMapper.selectOne(wrapper);
    }

    /**
     * @param name 单据名称
     * @description: 按名称筛选记录，如果名称为空，则返回全部
     * @author Elwin ZHANG
     * @date 2023/4/21 18:18
     */
    public List<SerialNo> getByName(String name) {
        QueryWrapper<SerialNo> wrapper = new QueryWrapper<>();
        String tenantId = ContextUtil.getCurrentTenantId();
        if (StringUtils.isNotEmpty(tenantId)) {
            wrapper.eq(Constants.COL_TENANT_ID, tenantId);
        } else {
            wrapper.and(query -> query.eq(COL_TENANT_ID, "").or().isNull(COL_TENANT_ID));
        }
        if (StringUtils.isNotEmpty(name)) {
            String key = name.trim();
            wrapper.and(query -> query.like("order_name", key).or().like("en_name", key).or().like("hk_name", key));
        }
        List<SerialNo> list = serialNoMapper.selectList(wrapper);
        return list;
    }

    /**
     * @param identifier 单据类型
     * @return java.lang.String
     * @description: 生成单号
     * @author Elwin ZHANG
     * @date 2022/5/20 18:24
     */
    private String generateNo(String identifier) {
        SerialNo sn = getByIdentifier(identifier);
        return generateNo(sn);
    }

    /**
     * @param pkId 单据类编号ID
     * @return java.lang.String
     * @description: 生成单号
     */
    private String generateNobyId(String pkId) {
        SerialNo sn = this.getById(pkId);
        return generateNo(sn);
    }

    /***
     * @description 根据单号设置生成新的单号
     * @param sn 单据编号对象
     * @return java.lang.String
     * @author Elwin ZHANG
     * @date 2023/4/21 15:32
     */
    private String generateNo(SerialNo sn) {
        //取查到当前公司的指定单据类型
        if (sn == null) {
            return "orderNo.identifierNotEmpty";
        }
        String rule = sn.getRules();
        Long curValue = sn.getCurrentVal();

        if (curValue == null) {
            curValue = (long) sn.getInitVal();
        }
        curValue += sn.getStep();
        sn.setCurrentVal(curValue);
        this.update(sn);
        return getByRule(rule, sn.getNoLen(), curValue);
    }


    /***
     * @description 删除序列号
     * @param id 记录ID
     * @author Elwin ZHANG
     * @date 2023/4/20 17:31
     */
    public void deleteSerialNo(String id) {
        var serialNo = this.serialNoMapper.selectById(id);
        if (serialNo == null) {
            return;
        }
        serialNo.setStatus((short) StatusEnum.deleted.getValue());
        serialNo.setSeq(serialNo.getSeq() + 1);
        serialNoMapper.updateById(serialNo);
    }

    @Override
    public BaseDao<SerialNo> getRepository() {
        return serialNoMapper;
    }
}